package com.kgcx.dbutils.provider;

import com.kgcx.common.constant.Cons;
import com.kgcx.common.utils.CollectionUtils;
import com.kgcx.dbutils.annotation.*;
import com.xiaoleilu.hutool.util.ReflectUtil;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.MappedStatement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.provider.base.BaseSelectProvider;

import java.lang.reflect.Field;
import java.util.Set;

/**
 * User : wuganglin
 * 定制查询器,主要是方便在sql执行过程中动态的植入一些参数
 * Date : 2017/12/1 11:30
 * version $Id: QueryInterceptor.java, v 0.1 Exp $
 */
public class SelectActiveProvider extends BaseSelectProvider {

    private static final Logger LOG = LoggerFactory.getLogger(SelectActiveProvider.class);

    public SelectActiveProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
        super(mapperClass, mapperHelper);
    }

    public String selectAllActive(MappedStatement ms) {
        return changeSql(ms, super.selectAll(ms), false);
    }

    public String selectActive(MappedStatement ms) {

        return changeSql(ms, super.select(ms), false);
    }

    public String selectOneActive(MappedStatement ms) {
        return changeSql(ms, super.selectOne(ms), false);
    }

    public String selectByPrimaryKeyActive(MappedStatement ms) {
        return changeSql(ms, super.selectByPrimaryKey(ms), true);
    }

    public String selectCountActive(MappedStatement ms) {
        return changeSql(ms, super.selectCount(ms), false);
    }

    public String existsWithPrimaryKeyActive(MappedStatement ms) {
        return changeSql(ms, super.existsWithPrimaryKey(ms), true);
    }

    public String changeSql(MappedStatement ms, String sql, boolean byPrimaryKey) {

        Class entityClass = getEntityClass(ms);
        if (!byPrimaryKey) {
            sql = sqlSpecial(sql, entityClass);
            sql = sqlCityIds(sql, entityClass);
        }
        sql = sqlStatus(sql, entityClass);


        return sql;
    }

    /**
     * 动态添加改变where条件
     *
     * @param sql
     * @param entityClass
     * @return
     */
    private String sqlSpecial(String sql, Class entityClass) {

        Set<EntityColumn> columns = EntityHelper.getColumns(entityClass);

        Field[] declaredFields = ReflectUtil.getFields(entityClass);
        if (ArrayUtils.isNotEmpty(declaredFields) && CollectionUtils.isNotEmpty(columns)) {
            for (Field declaredField : declaredFields) {
                for (EntityColumn column : columns) {
                    if (column.getProperty().equals(declaredField.getName())) {
                        String property = column.getProperty();
                        //模糊查询条件增加
                        //被SelectLike注解过的属性,添加 like 方式的过滤,参数名根据原参数名添加指定后缀,默认Like
                        if (declaredField.isAnnotationPresent(SelectLike.class)) {
                            SelectLike selectLike = declaredField.getAnnotation(SelectLike.class);
                            String likeName = property + selectLike.suffix();
                            String oriConditionalStr = column.getColumn() + " like CONCAT('%', #{" + likeName + "}, '%') ";
                            String conditionalStr = getIfNotNull(oriConditionalStr, likeName);
                            sql = sqlWhere(sql, conditionalStr);

                        }
                        //in查询条件增加
                        //被SelectIn注解过的属性,添加 in 方式的过滤,参数名根据原参数名添加指定后缀,默认In
                        if (declaredField.isAnnotationPresent(SelectIn.class)) {
                            SelectIn selectIn = declaredField.getAnnotation(SelectIn.class);
                            String inName = property + selectIn.suffix();
                            String oriConditionalStr = column.getColumn() + " in (${" + inName + "}) ";
                            String conditionalStr = getIfNotNull(oriConditionalStr, inName);
                            sql = sqlWhere(sql, conditionalStr);

                        }
                        //时间段查询条件增加
                        //被SelectBetweenAnd注解过的属性,添加 between ? and ? 方式的过滤,参数名根据原参数名添加指定后缀,默认Begin,End
                        if (declaredField.isAnnotationPresent(SelectBetweenAnd.class)) {
                            SelectBetweenAnd selectBetweenAnd = declaredField.getAnnotation(SelectBetweenAnd.class);
                            String betweenName = property + selectBetweenAnd.betweenSuffix();
                            String andName = property + selectBetweenAnd.andSuffix();
                            String oriConditionalStr = column.getColumn() + " between #{" + betweenName + "} " +
                                    " and #{" + andName + "} ";
                            String conditionalStr = getIfNotNull(oriConditionalStr, betweenName, andName);
                            sql = sqlWhere(sql, conditionalStr);
                        }
                        //大于条件增加
                        //被SelectGreaterThan注解过的属性,添加 > 方式的过滤,参数名根据原参数名添加指定后缀,默认Gt
                        if (declaredField.isAnnotationPresent(SelectGreaterThan.class)) {
                            SelectGreaterThan selectGreaterThan = declaredField.getAnnotation(SelectGreaterThan.class);
                            String gtName = property + selectGreaterThan.suffix();
                            String oriConditionalStr = column.getColumn() + " &gt; #{" + gtName + "} ";
                            String conditionalStr = getIfNotNull(oriConditionalStr, gtName);
                            sql = sqlWhere(sql, conditionalStr);
                        }
                        //大于条件增加
                        //被SelectGreaterThanOrEqual注解过的属性,添加 >= 方式的过滤,参数名根据原参数名添加指定后缀,默认Gt
                        if (declaredField.isAnnotationPresent(SelectGreaterThanOrEqual.class)) {
                            SelectGreaterThanOrEqual selectGreaterThanOrEqual = declaredField.getAnnotation(SelectGreaterThanOrEqual.class);
                            String geName = property + selectGreaterThanOrEqual.suffix();
                            String oriConditionalStr = column.getColumn() + " &gt;= #{" + geName + "} ";
                            String conditionalStr = getIfNotNull(oriConditionalStr, geName);
                            sql = sqlWhere(sql, conditionalStr);
                        }
                        //小于条件增加
                        //被SelectLessThan注解过的属性,添加 < 方式的过滤,参数名根据原参数名添加指定后缀,默认Lt
                        if (declaredField.isAnnotationPresent(SelectLessThan.class)) {
                            SelectLessThan selectLessThan = declaredField.getAnnotation(SelectLessThan.class);
                            String ltName = property + selectLessThan.suffix();
                            String oriConditionalStr = column.getColumn() + " &lt; #{" + ltName + "} ";
                            String conditionalStr = getIfNotNull(oriConditionalStr, ltName);
                            sql = sqlWhere(sql, conditionalStr);
                        }
                        //被SelectLessThanOrEqual注解过的属性,添加 <= 方式的过滤,参数名根据原参数名添加指定后缀,默认Lt
                        if (declaredField.isAnnotationPresent(SelectLessThanOrEqual.class)) {
                            SelectLessThanOrEqual selectLessThanOrEqual = declaredField.getAnnotation(SelectLessThanOrEqual.class);
                            String leName = property + selectLessThanOrEqual.suffix();
                            String oriConditionalStr = column.getColumn() + " &lt;= #{" + leName + "} ";
                            String conditionalStr = getIfNotNull(oriConditionalStr, leName);
                            sql = sqlWhere(sql, conditionalStr);
                        }

                        //被SelectOneOfThem注解过的属性,添加 FIND_IN_SET 方式的过滤,参数名根据原参数名添加指定后缀,默认Oot
                        if (declaredField.isAnnotationPresent(SelectOneOfThem.class)) {
                            SelectOneOfThem selectOneOfThem = declaredField.getAnnotation(SelectOneOfThem.class);
                            String ootName = property + selectOneOfThem.suffix();
                            String oriConditionalStr = " FIND_IN_SET( #{" + ootName + "}," + column.getColumn() + ") ";
                            String passWord = selectOneOfThem.passWord();
                            if (StringUtils.isNotBlank(passWord)) {
                                oriConditionalStr = "(" + oriConditionalStr + " or " + column.getColumn() + " = '" + passWord + "')";
                            }
                            String conditionalStr = getIfNotNull(oriConditionalStr, ootName);
                            sql = sqlWhere(sql, conditionalStr);
                        }

//                        if (declaredField.isAnnotationPresent(Sensitive.class)) {
//                            Sensitive sensitive = declaredField.getAnnotation(Sensitive.class);
//                            if (sensitive.isEncrypt()) {
//                                String oriName = property + "Ori";
//                                String columnEqualsHolder = column.getColumnEqualsHolder();
//                                String conditionalStr = "(" + columnEqualsHolder + " or " + column.getColumn() + " = #{" + oriName + "} )";
//                                sql = StringUtils.replace(sql, columnEqualsHolder, conditionalStr);
//                            }
//                        }
                    }
                }
            }
        }
        return sql;
    }

    /**
     * 动态添加status条件,只要是通过此查询器查询的sql都会自动只查询 101 状态的数据
     *
     * @param sql
     * @param entityClass
     * @return
     */
    private String sqlStatus(String sql, Class entityClass) {
        //只有存在status属性的实体类才添加status的where过滤

        Field status = ReflectionUtils.findField(entityClass, "status");
        if (null != status) {
            String conditionalStr = " and status = " + Cons.COMMON_STATUS_ACTIVE;
            sql = sqlWhere(sql, conditionalStr);
        }
        return sql;
    }

    /**
     * 动态添加cityIds过滤条件,只要是通过oa网关进来的请求
     * 会附带cityIds参数做数据隔离,此处添加where过滤条件语句
     * 具体的cityIds参数注入在mybatis拦截器QueryInterceptor处理.
     *
     * @param sql
     * @param entityClass
     * @return
     */
    private String sqlCityIds(String sql, Class entityClass) {
        //只有存在getCityId属性的实体类才添加getCityId的where过滤
        Field cityId = ReflectionUtils.findField(entityClass, "cityId");
        if (null != cityId) {
            String oriConditionalStr = " city_id in " +
                    "<foreach collection=\"cityIds\" item=\"i\" open=\"(\" separator=\",\" close=\")\">" +
                    "${i}" +
                    "</foreach> ";
            String conditionalStr = getIfNotNull(oriConditionalStr, "cityIds");
            sql = sqlWhere(sql, conditionalStr);
        }
        return sql;
    }

    /**
     * 把条件语句添加到where中去
     *
     * @param sql
     * @param conditionalStr
     * @return
     */
    private String sqlWhere(String sql, String conditionalStr) {

        String whereStart = "<where>";
        String whereEnd = "</where>";
        String whiteSpace = " ";
        //原sql中有<where>字段,则直接在</where>标签前追加条件语句
        if (StringUtils.contains(sql, whereStart)) {
            sql = StringUtils.replace(sql, whereEnd, whiteSpace + conditionalStr + whiteSpace + whereEnd);
        } else {
            //原sql中无<where>字段
            String orderBy = "ORDER BY";
            String limit = "LIMIT";

            if (StringUtils.containsIgnoreCase(sql, orderBy)) {
                //原sql中有order by段,则在order by前放入<where>条件</where>字段
                sql = StringUtils.substring(sql, 0, StringUtils.indexOfIgnoreCase(sql, orderBy))
                        + whiteSpace + whereStart + whiteSpace + conditionalStr + whiteSpace + whereEnd + whiteSpace
                        + StringUtils.substring(sql, StringUtils.indexOfIgnoreCase(sql, orderBy));
            } else if (StringUtils.containsIgnoreCase(sql, limit)) {
                //原sql中无order by但有limit段,,则在limit前放入<where>条件</where>字段
                sql = StringUtils.substring(sql, 0, StringUtils.indexOfIgnoreCase(sql, limit))
                        + whiteSpace + whereStart + whiteSpace + conditionalStr + whiteSpace + whereEnd + whiteSpace
                        + StringUtils.substring(sql, StringUtils.indexOfIgnoreCase(sql, limit));
            } else {
                //原sql中无order by无limit段,则直接在最尾处添加<where>条件</where>字段
                sql = sql + whiteSpace + whereStart + whiteSpace + conditionalStr + whiteSpace + whereEnd + whiteSpace;
            }
        }
        return sql;
    }

    /**
     * 给条件语句加上if test判断包裹,传入的条件表达式前面不需要加 and
     *
     * @param conditionalStr
     * @param properties
     * @return
     */
    private String getIfNotNull(String conditionalStr, String... properties) {

        StringBuilder str = new StringBuilder();
        if (ArrayUtils.isNotEmpty(properties)) {
            str.append("<if test=\"");
            for (int i = 0; i < properties.length; i++) {
                str.append(properties[i]).append(" != null ");
                if (i != properties.length - 1) {
                    str.append(" and ");
                }
            }
            str.append("\">");
            str.append(" and ").append(conditionalStr);
            str.append("</if>");
        }
        return str.toString();
    }

}
